﻿using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Paintball.Network
{
    public abstract class NetworkAgent
    {
        protected enum NetworkAgentMode { Server, Client }

        //Statics
        public static string ApplicationInstanceName = "DefaultAgent";
        public static readonly int SafePort = 1145;

        //Fields
        private NetworkAgentMode AgentType { get; set; }
        public bool Alive { get; set; }

        protected NetPeerConfiguration Configuration { get; set; }
        protected NetPeer NetworkPeer;

        public NetConnection Connection { get; protected set; }
        public bool IsConnected { get; set; }

        //ctor
        protected NetworkAgent(NetworkAgentMode type)
            : this(null, type)
        {
            
        }
        protected NetworkAgent(string appName, NetworkAgentMode type)
	    {
            ApplicationInstanceName = appName == null ? ApplicationInstanceName : appName;
            this.AgentType = type;
            Configuration = new NetPeerConfiguration(ApplicationInstanceName);
            CreateConfig(Configuration);

            if (AgentType == NetworkAgentMode.Server)
                NetworkPeer = new NetServer(Configuration);
            else if (AgentType == NetworkAgentMode.Client)
                NetworkPeer = new NetClient(Configuration);
	    }

        //Methods
            //Abstract
        protected abstract void CreateConfig(NetPeerConfiguration config);
        protected abstract void HandleMessage(NetIncomingMessageType type, NetIncomingMessage msg);
        
        private void HandleDataMessage(NetIncomingMessage msg)
        {
            //look at the first byte in the msg, if it is set to 0 - pass message on to be handled another way

            if (msg.ReadByte() == 0)
                HandleUnhandledDataMessage(msg);
            else
            {
                //else it is a IDataPacket, so cast and Decode
            }
        }
        protected abstract void HandleUnhandledDataMessage(NetIncomingMessage msg);

        public void Start()
        {
            if (NetworkPeer == null)
                throw new SystemException("Network peer apear to be null :(");
            if (Alive)
                throw new SystemException("Cannot start host, host has already been started");

            NetworkPeer.Start();
            Alive = true;
            ThreadPool.QueueUserWorkItem(
                delegate 
                {
                    while (Alive)
                    {
                        NetIncomingMessage inc = null;
                        while ((inc = NetworkPeer.ReadMessage()) != null)
                        {
                            if (inc.MessageType == NetIncomingMessageType.Data)
                                HandleDataMessage(inc);
                            else
                                HandleMessage(inc.MessageType, inc);

                            NetworkPeer.Recycle(inc);
                        }
                    }
                });

            InitalizeServer();
        }
        public abstract void InitalizeServer();
        
        //Helpers
        public T Peer<T>()
            where T : NetPeer
        {
            return (T)NetworkPeer;
        }
    }
}
